iT邦幫忙

2025 iThome 鐵人賽

DAY 6
0
Software Development

重啟挑戰:老派軟體工程師的測試修練系列 第 6

Day 06:Code Coverage 程式碼涵蓋範圍實戰指南

  • 分享至 

  • xImage
  •  

前言

寫測試程式時,經常會遇到這樣的問題:「我寫的測試夠嗎?」、「還有哪些程式碼沒有被測試到?

Code Coverage(程式碼涵蓋範圍) 是用來回答這些問題的指標。它會告訴你測試執行時,實際覆蓋了多少比例的程式碼。

但需要先說明一個重點:Code Coverage 不是萬能的。100% 的涵蓋率不代表沒有 Bug,也不表示測試品質良好。它只是一個輔助指標,幫助你找出可能遺漏的測試區域。

今天會介紹如何在不同開發環境中使用 Code Coverage 工具,包括 VS Code 和 Visual Studio 的相關工具設定,以及如何正確解讀涵蓋率報告。

學習目標

  • 理解 Code Coverage 的實際用途和限制
  • 學會在 VS Code 中使用測試覆蓋率功能
  • 掌握 Visual Studio 測試總管的操作
  • 設定 Fine Code Coverage 擴充套件
  • 解讀涵蓋率報告並制定改善策略
  • 結合程式碼複雜度指標評估測試需求

Code Coverage 基本概念

程式碼涵蓋範圍是一種測量指標,用來統計測試執行時實際執行了多少程式碼。

  • 為有效防範 Bug,你的測試應該要使用或「覆蓋」大部分的程式碼。
  • 若要判斷單元測試等自動程式碼測試實際測試的專案程式碼比例,可以使用 Visual Studio 程式碼涵蓋範圍功能。

常見誤解

錯誤認知:

  • 涵蓋率 100% 就沒有 Bug
  • 涵蓋率數字越高越好
  • 可以用涵蓋率當作 KPI

正確認知:

  • Code Coverage 只是提醒工具,告訴你哪些程式碼沒被測試
  • 重點是測試的有效性,不是覆蓋率數字
  • 幫助判斷是否需要補充測試案例
  • 絕對不應該當作 KPI 使用

當 Code Coverage 被當作 KPI 時,開發者會為了衝數字而寫沒有 Assert 的測試,完全失去了測試的意義。

Code Coverage 的實際價值

  1. 找出測試盲點:快速識別沒有被測試的程式碼
  2. 評估測試完整性:檢查重要邏輯是否都有測試
  3. 輔助重構決策:了解哪些區域需要更多關注
  4. 增加測試信心:確認關鍵路徑都有被驗證

延伸閱讀:


Code Coverage 工具介紹

Visual Studio Enterprise 內建功能

Visual Studio 有內建的程式碼涵蓋範圍功能:

限制:

day06 visualstudio enterprise codecoverage

命令列工具

也可以用 .NET CLI 工具:

# 安裝工具
dotnet tool install -g dotnet-coverage

# 執行測試並產生報告
dotnet-coverage collect dotnet test

相關文檔:

限制:

  • 要記一堆指令
  • 流程比較麻煩
  • 不夠直觀

Fine Code Coverage 擴充套件

為什麼用 Fine Code Coverage

沒有 Visual Studio Enterprise 的話,安裝 Visual Studio Extensions - Fine Code Coverage 是最好的選擇:

  • 免費使用
  • 整合在 Visual Studio 裡
  • 測試執行後馬上顯示結果
  • 程式碼編輯器直接標示覆蓋狀況

day06 fine code coverage 01

相關連結:

安裝設定

  1. 安裝擴充套件
    • 開啟 Visual Studio
    • 延伸模組 → 管理延伸模組
    • 搜尋 "Fine Code Coverage"
    • 安裝後重新啟動

day06 fine code coverage 02

  1. 開啟功能視窗
    • 檢視 → 其他視窗 → Fine Code Coverage

day06 fine code coverage 03

  1. 功能視窗

day06 fine code coverage 04

Fine Code Coverage 設定

  1. 開啟選項:工具 → 選項 → Fine Code Coverage

  2. 啟用功能

    • Run (Common) → Enable:設為 True
    • Editor Colouring Line Highlighting:設為 True

day06 fine code coverage 05


Fine Code Coverage 涵蓋率報告分析

執行測試並檢視報告

設定完成後,執行所有測試:

day06 fine code coverage 06

解讀涵蓋率報告

測試執行完,Fine Code Coverage 會顯示報告:

day06 fine code coverage 07

視覺化程式碼覆蓋狀況

啟用編輯器指示器:工具 > FCC Toggle Indicators

day06 fine code coverage 08

顏色標示

程式碼編輯器中的顏色標示:

  • 綠色:已被測試覆蓋
  • 黃色:部分覆蓋
  • 紅色:未被覆蓋

day06 fine code coverage 09

圖片來源: Fine Code Coverage - Visual Studio Marketplace

改善策略

根據報告進行改善:

  1. 優先處理紅色區域:完全沒被測試的程式碼
  2. 檢查黃色區域:確認所有條件分支都有測試
  3. 評估必要性:簡單的 getter/setter 可能不需要測試

Fine Code Coverage 無法產生 Code Coverage 報告的解決方式

在某些 Windows 作業系統環境下,.NET 8/9 的安全性更新可能會讓 Fine Code Coverage 無法正常運作,可參考以下文章內容進行相關設定:

詳細解決方法參考:


VS Code 測試覆蓋率功能

如果你使用 VS Code 開發,也可以在裡面進行測試覆蓋率分析。VS Code 的測試功能支援多種語言和框架,對跨平台開發很有用。

VS Code 測試功能特色

  • 支援多種語言:JavaScript、TypeScript、Python、Java、C# 等
  • 豐富的擴充套件:支援 Jest、Mocha、Pytest、JUnit 等測試框架
  • 整合式管理:Test Explorer 提供統一的測試管理界面
  • 內建覆蓋率檢視:可以直接查看覆蓋率結果

開始使用

  1. 安裝測試擴充套件

    • Ctrl+Shift+X 開啟擴充功能
    • 搜尋適合你專案的測試擴充套件 (dotnet C# 就安裝 C# Dev Kit)
  2. 開啟測試總管

    • 點選活動列的燒杯圖示
    • 或執行命令:Testing: Focus on 測試總管 View
  3. 相關連結

測試覆蓋率功能

VS Code 提供幾種方式檢視覆蓋率:

執行覆蓋率測試

測試總管 中可以:

  • 點選「執行涵蓋範圍測試
  • 使用編輯器邊欄的覆蓋率控制項「以涵蓋範圍執行
  • 透過命令面板執行覆蓋率指令

day06 vscode run with coverage

day06 fine code coverage 10

測試涵蓋範圍

顯示樹狀結構的覆蓋率資訊:

  • 各檔案的覆蓋率百分比
  • 用顏色表示覆蓋率等級
  • 滑鼠懸停可看詳細資料

day06 fine code coverage 11

編輯器內顯示

直接在程式碼中標示:

  • 綠色:已覆蓋的程式碼
  • 紅色:未覆蓋的程式碼
  • 執行次數:顯示每行被執行的次數
  • 切換顯示:Test: Show Inline Coverage (測試: 切換內嵌涵蓋範圍) (Ctrl+; Ctrl+Shift+I)

day06 fine code coverage 12

檔案總管顯示

在檔案總管中直接顯示各檔案的覆蓋率百分比,方便快速識別需要加強測試的檔案。

day06 fine code coverage 13

VS Code 測試設定

可以調整的設定項目:

設定 用途
testing.countBadge 活動列測試圖示的計數顯示
testing.gutterEnabled 編輯器邊欄測試控制項
testing.defaultGutterClickAction 邊欄控制項預設動作
testing.coverageBarThresholds 覆蓋率顏色閾值
testing.displayedCoveragePercent 覆蓋率百分比顯示類型
testing.showCoverageInExplorer 檔案總管覆蓋率顯示

VS Code vs Visual Studio

項目 VS Code Visual Studio
平台支援 Windows, macOS, Linux 主要 Windows
語言支援 多語言(透過擴充) 主要 .NET 語言
測試框架 廣泛支援 MSTest, xUnit, NUnit
覆蓋率工具 內建支援 Enterprise 或第三方
適用場景 跨平台、多語言 .NET 企業級專案

程式碼複雜度評估

以下工具為 Visual Studio 的 Extensions (不是 VS Code 的 Extensions)

CodeMaintainability 擴充套件

除了 Code Coverage,還可以用程式碼可維護性指標評估測試需求:

day06 CodeMaintainability 01

可維護性指標

CodeMaintainability 提供的指標:

day06 CodeMaintainability 02

  1. Maintainability Index (0-100):可維護性指數

    • 0-9:差
    • 10-19:中等
    • 20-100:良好
  2. Cyclomatic Complexity:循環複雜度

    • 程式的邏輯路徑數量
    • 數值越高,需要的測試案例越多
  3. Halstead Volume:程式碼體積

    • 操作符和操作數的複雜度
  4. Lines of Code:程式碼行數

循環複雜度(Cyclomatic Complexity)與測試案例的關係

循環複雜度不僅是衡量程式碼邏輯複雜度的指標,也是一個非常實用的參考依據,可以用來判斷「至少」要寫多少個單元測試案例。

為什麼循環複雜度可以當作測試案例的下限?

循環複雜度的數值代表了程式中所有獨立邏輯路徑(independent paths)的數量。為了達到 完整的邏輯覆蓋(branch coverage),理論上你需要撰寫至少等同於循環複雜度數值的測試案例,以涵蓋所有可能的邏輯分支。

Max 方法範例

public int Max(int[] array)
{
    if (array == null || array.Length == 0)
    {
        throw new ArgumentException("array must not be empty.");
    }

    int max = array[0];

    for (int i = 1; i < array.Length; i++)
    {
        if (array[i] > max)
        {
            max = array[i];
        }
    }

    return max;
}

在實務中,我們可以用一個簡化的方式來估算:

每個條件判斷(如 if、for、while、case、&&、||)都會增加 1

Max 方法的循環複雜度分析:

Max 方法的循環複雜度分析

Max 這個方法的 循環複雜度為 5,代表它有 5 條獨立的邏輯路徑。這也表示:

  • 至少需要 5 個單元測試案例 才能涵蓋所有邏輯分支 (每個邏輯路徑都被執行過一次)。
  • 如果想要達到 100% branch coverage,這個數字就是一個很好的起點。

測試案例設計:

以下是根據 Max(int[] array) 方法的循環複雜度為 5 所設計的 5 個單元測試案例,每個案例都對應一條獨立的邏輯路徑,確保涵蓋所有條件與分支:

測試案例 測試內容 涵蓋路徑
Max_傳入null_應拋出例外 傳入 null array == null
Max_傳入空陣列_應拋出例外 傳入空陣列 array.Length == 0
Max_陣列只有單一元素_應回傳該元素 單一元素 不進入迴圈
Max_最大值在開頭_應回傳最大值 最大值在開頭 迴圈不更新 max
Max_最大值在中間_應回傳最大值 最大值在中間 迴圈更新 max

CodeMaid Spade 視覺化分析

CodeMaid 簡介

CodeMaid 是另一個實用的 Visual Studio 擴充套件:

day06 codemaid 01

Spade 功能

day06 codemaid 02

開啟 CodeMaid Spade 檢視程式碼結構:

day06 codemaid 03

Spade 功能:

  • 顯示類別成員的循環複雜度
  • 幫助識別需要重構的程式碼

實際專案的複雜度警告

實際專案中可能出現高複雜度警告:

day06 CodeMaintainability 03

當看到紅色數字時:

  • 該方法邏輯過於複雜
  • 需要大量測試案例來覆蓋所有路徑
  • 應該考慮重構

影響:

  • 難以撰寫完整的單元測試
  • 維護成本高
  • 出錯風險增加

實戰建議

測試案例數量決策

  1. 基於需求分析

    • 列出方法的使用案例
    • 識別邊界條件和異常情況
    • 考慮業務邏輯的各種情境
  2. 參考複雜度指標

    • 循環複雜度提供測試案例下限
    • 高複雜度方法需要更多測試
    • 考慮重構降低複雜度
  3. 平衡覆蓋率與品質

    • 不以 100% 覆蓋率為唯一目標
    • 專注於關鍵業務邏輯
    • 確保測試的實際價值

測試策略

  1. 邊界測試:測試輸入值的上下限
  2. 異常測試:驗證錯誤處理邏輯
  3. 主流程測試:覆蓋正常的業務流程
  4. 條件分支測試:確保所有分支都有測試

持續改善

  1. 定期檢視報告:每次提交前檢查涵蓋率變化
  2. 識別風險區域:關注未覆蓋的關鍵程式碼
  3. 漸進式改善:逐步提升重要模組的測試覆蓋率
  4. 團隊協作:建立測試標準和流程

總結

今天的重點:

  • Code Coverage 的正確認知:當作工具而非目標
  • 多平台解決方案:VS Code 和 Visual Studio 的覆蓋率功能
  • 實用工具設定:Fine Code Coverage 和相關擴充套件 (CodeMaintainability, CodeMaid)

老派工程師的心得感想

到底要寫多少測試案例才足夠?

單元測試案例的數量可以根據程式碼的複雜度和重要性來決定。可以使用以下方法來確定測試案例的數量:

  • 邊界測試:測試輸入值的上下限,確保系統在極端情況下能正常運行。
  • 驗證測試:確保輸入值符合預期格式和範圍。
  • 條件判斷測試:測試不同條件下的分支路徑,確保每個分支都能正確執行。

CodeMaid 對於寫單元測試的幫助?

從前面的介紹與文章內容可以得知 CodeMaid 是對於開發人員在編寫程式碼時的一個輔助工具,但似乎沒有提到對於寫單元測試有什麼直接的幫助。

前面一直都有提到很多開發人員對於單元測試最在乎的一件事「要寫多少的測試案例?

可以藉由 CodeMaid 所提供的一個小功能來得到一個粗略的數字,而這個數字不是一個相當準確的答案,但至少可以讓開發人員藉此來取得「至少要寫多少個測試案例」的參考依據。

個人建議

我的建議會是開發者依據需求 (SA 或 SD 規格),去寫出功能的 使用案例,就是這個功能的 使用說明書,以使用這個方法的 User 角度去描述這個功能要怎麼使用,例如:

  • 當輸入錯誤或沒有輸入資料的情境下,方法會有什麼錯誤訊息或回傳什麼樣的資料。
  • 當輸入不符合規格的資料後,方法執行會回傳什麼樣的錯誤訊息。
  • 當輸入符合規格的資料後,方法執行會回傳什麼樣的結果。
  • 當輸入符合規格的資料後,遇到一些情境或狀態的判斷後,影響後續回傳結果。

而這些使用案例逐一列出來之後,我們就會知道程式會怎麼開發、要寫哪些的測試案例來驗證實作程式碼。而這些使用案例也就可以轉換作為測試案例。

實際開發的情況

實際的工作專案可能就無法依據 Code Maintainability 或 Code Metric 就可以判斷要寫多少測試案例,複雜度與可維護性的數據就只能當作參考。

專案開發時就會真的需要將使用案例逐一列出,接著依據需求規格與使用案例將程式實作出來,在實作的過程中就會知道有哪些情境會需要測試。

要寫多少個測試案例並沒有一個標準,甚至連達到 100% 程式碼涵蓋率的程式碼也並不表示就寫完了所有的測試案例。因為資料的多樣性與變化、需求規格的複雜等因素,對程式開發人員來說也只能盡量地讓已知的測試情境去寫出來。

系統能夠正確地運作執行並不代表該系統的品質就是好的,只是都沒有執行到會出錯的情境而已,一旦出現了當初設計、開發時沒有設想到的情境時,系統就會出現錯誤,而這就是系統的 BUG,然後為了要修復這個 BUG 而要去修改程式碼。

對一個沒有單元測試覆蓋的程式碼去做異動,就如同矇著眼睛走進一個到處埋著地雷的危險地帶。沒有單元測試的保護,異動程式碼任何一個地方都無法保證不會對既有功能有影響,甚至於影響到什麼地方也都充滿著未知。

所以

  • 盡可能地為實作程式碼寫單元測試
  • 盡可能地降低程式碼的循環複雜度

明日預告

目前我們已經掌握測試基礎、xUnit 框架、Awesome Assertions 和 Code Coverage。但寫單元測試時,經常遇到一個問題:如何處理外部相依性?

當程式碼需要存取資料庫、呼叫 API、讀取檔案或使用系統時間時,這些相依性會讓測試:

  • 執行緩慢且結果不穩定
  • 依賴特定環境才能執行
  • 無法控制測試情境

明天會介紹 「依賴替代入門:使用 NSubstitute」

  • NSubstitute 實作技巧:如何替代外部相依性
  • 實際應用:模擬資料庫存取、API 呼叫等常見情境
  • 程式設計原則:如何讓程式碼更容易測試
  • Test Double 基本概念:Dummy、Stub、Fake、Spy、Mock 的差異

這是從基礎測試進階到實用單元測試的關鍵步驟。


參考資料

官方文件

工具

相關連結

範例程式碼:


這是「重啟挑戰:老派軟體工程師的測試修練」的第六天。明天會介紹 Day 07:依賴替代入門 - 使用 NSubstitute。


上一篇
Day 05:AwesomeAssertions 進階技巧與複雜情境應用
下一篇
Day 07:依賴替代入門 - 使用 NSubstitute
系列文
重啟挑戰:老派軟體工程師的測試修練24
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言